#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Picture.h"

#include "Class.h"
#include "String.h"
#include "ObjArray.h"
#include "WindowSystem.h"
#include "PictPort.h"

//---- Picture -----------------------------------------------------------------

NewMetaImpl(Picture,Object, (TV(buf,length), T(bbox), TP(map)));

Picture::Picture()
{
	buf= 0;
	map= 0;
}

Picture::Picture(const Rectangle &b, byte *pagebuf, long len, ObjArray *m)
{
	Set(b, pagebuf, len, m);
}

Picture::~Picture()
{
	SafeDelete(buf);
	SafeDelete(map);
}

void Picture::Set(const Rectangle &b, byte *pagebuf, long len, ObjArray *m)
{
	SafeDelete(buf);
	SafeDelete(map);
	map= m;
	length= (int) len;
	bbox= b;
	if (length > 0) {
		buf= new byte[length];
		MemCpy(buf, pagebuf, (u_int) length);
	} else
		buf= 0;
}

OStream &Picture::PrintOn(OStream &os)
{
	Object::PrintOn(os);
	os << bbox SP << map SP;
	return os.PrintHexString(buf, length);
}

IStream &Picture::ReadFrom(IStream &is)
{
	int l;
	Object::ReadFrom(is);
	is >> bbox >> map;
	is.ReadString(&buf, &l);
	length= l;
	return is;
}

static const Point &GetPoint(IStream &is, byte code)
{
	static Point p;
	
	switch (code & 0x0c) {
	case 0x00:
		p.x= 0;
		break;
	case 0x04:
		p.x= is.GetChar();
		break;
	case 0x08:
		p.x= (short) is.GetBigEndian(2);
		break;
	case 0x0c:
		p.x= (int) is.GetBigEndian(4);
		break;
	}
	switch (code & 0x03) {
	case 0x00:
		p.y= 0;
		break;
	case 0x01:
		p.y= is.GetChar();
		break;
	case 0x02:
		p.y= (short) is.GetBigEndian(2);
		break;
	case 0x03:
		p.y= (int) is.GetBigEndian(4);
		break;
	}
	return p;
}

static Object *GetObject(IStream &is, ObjArray *map, Object *dflt= 0)
{
	int ix= (int)is.GetBigEndian(2);
	if (ix < 0 || ix >= map->Size())
		return dflt;
	Object *op= map->At(ix);
	return op ? op : dflt;
}

void Picture::Show(const Rectangle &rp, register Port *port)
{
	register int i;
	Rectangle r;
	Ink *ink= gInkBlack;
	byte b, hintbuf[1000];
	GrLineCap cap= eDefaultCap;
	GrPolyType polytype;
	Picture *pict;
	Font *font= gSysFont;
	int lastlen= 0, psz= 1, startangle= 0, endangle= 0, code, len;
	Point dia, p, *pts= 0;
	Bitmap *bm;
	bool done= FALSE;
	MemBuf mb(length, (char*)buf, length);
	IStream is(&mb);

	GrState gr(port);

	port->Scale(1.0/port->GetXScale(), 1.0/port->GetYScale());
	port->Translate(rp.origin /* - bbox.origin */ - port->GetOrigin());
	port->Scale((float) rp.extent.x / (float) bbox.extent.x,
				(float) rp.extent.y / (float) bbox.extent.y);

	while (!done && is.get(b)) {
		switch (b) {
		case cPicEnd:
			done= TRUE;
			continue;
		case cPicClip:
			port->Clip(gr.clip);
			port->ClipFurther(r);
			continue;
		case cPicFont:
			font= (Font*) GetObject(is, map, gSysFont);
			continue;
		case cPicInk:
			ink= (Ink*) GetObject(is, map, gInkBlack);
			continue;
		case cPicStartAngle:
			startangle= (short) is.GetBigEndian(2);
			continue;
		case cPicEndAngle:
			endangle= (short) is.GetBigEndian(2);
			continue;

		case cPicExtent: case cPicExtent+1: case cPicExtent+2: case cPicExtent+3:
		case cPicExtent+4: case cPicExtent+5: case cPicExtent+6: case cPicExtent+7:
		case cPicExtent+8: case cPicExtent+9: case cPicExtent+10: case cPicExtent+11:
		case cPicExtent+12: case cPicExtent+13: case cPicExtent+14: case cPicExtent+15:
			r.extent+= GetPoint(is, b);
			continue;

		case cPicMove: case cPicMove+1: case cPicMove+2: case cPicMove+3:
		case cPicMove+4: case cPicMove+5: case cPicMove+6: case cPicMove+7:
		case cPicMove+8: case cPicMove+9: case cPicMove+10: case cPicMove+11:
		case cPicMove+12: case cPicMove+13: case cPicMove+14: case cPicMove+15:
			r.origin+= GetPoint(is, b);
			continue;

		case cPicLine: case cPicLine+1: case cPicLine+2: case cPicLine+3:
		case cPicLine+4: case cPicLine+5: case cPicLine+6: case cPicLine+7:
		case cPicLine+8: case cPicLine+9: case cPicLine+10: case cPicLine+11:
		case cPicLine+12: case cPicLine+13: case cPicLine+14: case cPicLine+15:
			p= r.origin;
			r.origin+= GetPoint(is, b);
			port->StrokeLine(ink, psz, cap, p, r.origin);
			break;

		case cPicCornerDia: case cPicCornerDia+1: case cPicCornerDia+2: case cPicCornerDia+3:
		case cPicCornerDia+4: case cPicCornerDia+5: case cPicCornerDia+6: case cPicCornerDia+7:
		case cPicCornerDia+8: case cPicCornerDia+9: case cPicCornerDia+10: case cPicCornerDia+11:
		case cPicCornerDia+12: case cPicCornerDia+13: case cPicCornerDia+14: case cPicCornerDia+15:
			dia= GetPoint(is, b);
			break;

		case cPicPensize:
			psz= is.GetByte();
			continue;
		case cPicPenCap:
			cap= (GrLineCap) is.GetByte();
			continue;
		case cPicStrokeRect:
			port->StrokeRect(ink, psz, r);
			continue;
		case cPicStrokeRRect:
			port->StrokeRRect(ink, psz, r, dia);
			continue;
		case cPicStrokeOval:
			port->StrokeOval(ink, psz, r);
			continue;
		case cPicStrokeWedge:
			port->StrokeWedge(ink, psz, cap, r, startangle, endangle);
			continue;
		case cPicFillPoly:
		case cPicStrokePoly:
			polytype= (GrPolyType) is.GetByte();
			len= (short) is.GetBigEndian(2);
			if (len > lastlen) {
				SafeDelete(pts);
				pts= new Point[lastlen= len];
			}
			for (i= 0; i < len; i++)
				pts[i]= GetPoint(is, is.GetByte());
			if (b == cPicStrokePoly)
				port->StrokePolygon(r.origin, ink, pts, len, polytype, psz, cap);
			else
				port->FillPolygon(r.origin, ink, pts, len, polytype);
			continue;
		case cPicFillRect:
			port->FillRect(ink, r);
			continue;
		case cPicFillOval:
			port->FillOval(ink, r);
			continue;
		case cPicFillRRect:
			port->FillRRect(ink, r, dia);
			continue;
		case cPicFillWedge:
			port->FillWedge(ink, r, startangle, endangle);
			continue;
		case cPicShowBitmap:
			if (bm= (Bitmap*) GetObject(is, map))
				port->ShowBitmap(ink, r, bm);
			else
				port->StrokeRect(gInkBlack, 1, r);
			continue;
		case cPicShowPicture:
			if (pict= (Picture*) GetObject(is, map))
				port->ShowPicture(r, pict);
			else
				port->StrokeRect(gInkBlack, 1, r);
			continue;
		case cPicHint:
			code= (int) is.GetBigEndian(4);
			len= (int) is.GetBigEndian(4);
			for(i= 0; i < len; i++)
				is.get(hintbuf[i]);
			port->GiveHint(code, len, len > 0 ? hintbuf : 0);
			continue;
		case cPicXMove0:    case cPicXMove0+1:  case cPicXMove0+2:  case cPicXMove0+3:
		case cPicXMove0+4:  case cPicXMove0+5:  case cPicXMove0+6:  case cPicXMove0+7:
		case cPicXMove0+8:  case cPicXMove0+9:  case cPicXMove0+10: case cPicXMove0+11:
		case cPicXMove0+12: case cPicXMove0+13: case cPicXMove0+14: case cPicXMove0+15:
		case cPicXMove0+16: case cPicXMove0+17: case cPicXMove0+18: case cPicXMove0+19:
		case cPicXMove0+20: case cPicXMove0+21: case cPicXMove0+22: case cPicXMove0+23:
		case cPicXMove0+24: case cPicXMove0+25: case cPicXMove0+26: case cPicXMove0+27:
		case cPicXMove0+28: case cPicXMove0+29: case cPicXMove0+30: case cPicXMove0+31:
		case cPicXMove0+32: case cPicXMove0+33: case cPicXMove0+34: case cPicXMove0+35:
		case cPicXMove0+36: case cPicXMove0+37: case cPicXMove0+38: case cPicXMove0+39:
		case cPicXMove0+40: case cPicXMove0+41: case cPicXMove0+42: case cPicXMove0+43:
		case cPicXMove0+44: case cPicXMove0+45: case cPicXMove0+46: case cPicXMove0+47:
		case cPicXMove0+48: case cPicXMove0+49: case cPicXMove0+50: case cPicXMove0+51:
		case cPicXMove0+52: case cPicXMove0+53: case cPicXMove0+54: case cPicXMove0+55:
		case cPicXMove0+56: case cPicXMove0+57: case cPicXMove0+58: case cPicXMove0+59:
		case cPicXMove0+60: case cPicXMove0+61: case cPicXMove0+62: case cPicXMove0+63:
			r.origin.x+= b-cPicXMove0-32;
			continue;
		case cPicEsc:
			b= is.GetByte();
		default:
			r.origin.x+= port->ShowChar(font, ink, r.origin, b);
			continue;
		}
	}
}

void Picture::CollectParts(Collection *col)
{
	if (map)
		col->Add(map);
}

